/**
 * 
 */
package org.apache.james.hibernate;

import java.util.HashMap;
import java.util.Map;

import org.apache.james.hibernate.entity.ImapUserInfo;
import org.apache.james.hibernate.entity.NamespaceImpl;
import org.apache.james.mailboxmanager.MailboxManagerException;
import org.apache.james.mailboxmanager.Namespace;
import org.apache.james.mailboxmanager.Namespaces;
import org.apache.james.mailboxmanager.mailbox.MailboxSession;
import org.apache.james.mailboxmanager.manager.MailboxManager;
import org.apache.james.mailboxmanager.manager.MailboxManagerProvider;
import org.apache.james.mailboxmanager.tracking.MailboxCache;
import org.apache.james.services.JamesUser;
import org.apache.james.services.User;
import org.apache.log4j.Logger;

/**
 * @author zsombor
 * 
 */
public class HibernateMailboxManagerProvider implements MailboxManagerProvider {

	public static final char HIERARCHY_DELIMITER = '.';

//	public static final String USER_NAMESPACE = "#mail";

	public static final String INBOX = "INBOX";

	private final static Logger LOG = Logger
			.getLogger(HibernateMailboxManagerProvider.class);

	MailboxPersistenceManager mailboxManager;

    private MailboxCache mailboxCache = new MailboxCache();

    Map<String, HibernateMailboxManager> mailboxManagerMap = new HashMap<String, HibernateMailboxManager>();
	
	public HibernateMailboxManagerProvider() {
	}

	public void setDbMailboxManager(MailboxPersistenceManager mailboxManager) {
		this.mailboxManager = mailboxManager;
	}

	public void init() {
		// setup the db-only solution
		mailboxManager.setupMailStore(null);
		
		if (mailboxManager.getNamespace("mail") == null) {
			mailboxManager.createNewNamespace("mail",
					"Default user mail namespace", "", false, true);
		}
		if (mailboxManager.getNamespace("shared") == null) {
			NamespaceImpl sharedNs = mailboxManager.createNewNamespace("shared",
					"Default shared mail namespace", "", true, false);
			
			mailboxManager.createMailbox(null, sharedNs, sharedNs.getMailboxName("global"));
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.james.mailboxmanager.manager.MailboxManagerProvider#deleteAllUserData(org.apache.james.services.User,
	 *      org.apache.james.services.User)
	 */
	@Override
	public void deleteAllUserData(User authUser, User targetUser) {

		LOG.info("deleteAllUserData (authUser, targetUser):");
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.james.mailboxmanager.manager.MailboxManagerProvider#deleteEverything()
	 */
	@Override
	public void deleteEverything() throws MailboxManagerException {

		LOG.info("deleteEverything ():");
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.james.mailboxmanager.manager.MailboxManagerProvider#getInboxSession(org.apache.james.services.User)
	 */
	@Override
	public MailboxSession getInboxSession(User user)
			throws MailboxManagerException {

		LOG.info("getInboxSession (user):" + user.getUserName());
		return getMailboxManagerInstance(user).getMailboxSession(
				getInboxName(user), true);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.james.mailboxmanager.manager.MailboxManagerProvider#getMailboxManagerInstance(org.apache.james.services.User)
	 */
	@Override
	public MailboxManager getMailboxManagerInstance(User user)
			throws MailboxManagerException {
		LOG.info("getMailboxManagerInstance (user):" + user.getUserName());
		synchronized(mailboxManagerMap) {
			// I'm not sure this is correct, to cache this objects, because close is not necessarily called.
			// TODO: profile for HibernateMailboxManager leakages!
			HibernateMailboxManager manager = mailboxManagerMap.get(user.getUserName());
			if (manager==null) {
				manager = new HibernateMailboxManager(this, mailboxManager, user, mailboxCache);
				mailboxManagerMap.put(user.getUserName(), manager);
			}
			return manager;
		}
	}

	void removeMailboxManager(User forUser) {
		synchronized(mailboxManagerMap) {
			mailboxManagerMap.remove(forUser.getUserName());
		}
	}
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.james.mailboxmanager.manager.MailboxManagerProvider#getMailboxSession(org.apache.james.services.User,
	 *      java.lang.String, boolean)
	 */
	@Override
	public MailboxSession getMailboxSession(User authUser, String mailboxName,
			boolean autoCreate) throws MailboxManagerException {
		LOG.info("getMailboxSession (authUser, mailboxName, autoCreate):"+authUser.getUserName()+','+mailboxName+','+autoCreate);
        return getMailboxManagerInstance(authUser).getMailboxSession(
                mailboxName, autoCreate);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.james.mailboxmanager.manager.MailboxManagerProvider#getNamespaces(org.apache.james.services.User)
	 */
	@Override
	public Namespaces getNamespaces(User forUser) {
		LOG.info("getNamespaces (forUser):" + forUser.getUserName());
		ImapUserInfo userInfo = getImapUserInfo(forUser);

		return userInfo.getNamespaceInfo();
	}

	ImapUserInfo getImapUserInfo(User forUser) {
		ImapUserInfo userInfo = null;
		if (forUser instanceof JamesUser) {
			userInfo = (ImapUserInfo) ((JamesUser) forUser)
					.getAttribute("imapuser");
		}
		if (userInfo == null) {
			userInfo = mailboxManager
					.getUserinfo(forUser.getUserName());
		}
		if (forUser instanceof JamesUser) {
			((JamesUser) forUser).setAttribute("imapuser", userInfo);
		}
		return userInfo;
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.james.mailboxmanager.manager.MailboxManagerProvider#getOpenMailboxSessionCountMap()
	 */
	@Override
	public Map getOpenMailboxSessionCountMap() {
		LOG.info("getOpenMailboxSessionCountMap ():");
		return mailboxCache.getOpenMailboxSessionCountMap();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.james.mailboxmanager.manager.MailboxManagerProvider#getPersonalDefaultNamespace(org.apache.james.services.User)
	 */
	@Override
	public Namespace getPersonalDefaultNamespace(User forUser) {
		LOG.info("getPersonalDefaultNamespace (forUser):"
				+ forUser.getUserName());
		return getNamespaces(forUser).getPersonalDefault();
	}

	String getInboxName(User user) {
		Namespace nameSpace = getPersonalDefaultNamespace(user);
		return nameSpace.getName() + nameSpace.getHierarchyDelimter() + INBOX;
	}

}
